package wicket.example.components.table.test.base;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
import org.apache.wicket.markup.head.CssHeaderItem;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.markup.html.TransparentWebMarkupContainer;
import org.apache.wicket.markup.html.navigation.paging.PagingNavigator;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.RefreshingView;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.util.ModelIteratorAdapter;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.request.resource.CssResourceReference;
import org.apache.wicket.request.resource.PackageResourceReference;

import wicket.example.components.table.test.columns.DataTableColumn;
import wicket.example.components.table.test.headers.LabelHeader;
import wicket.example.components.table.test.headers.SortingHeader;
import wicket.example.components.table.test.navigator.DropDownEntry;
import wicket.example.components.table.test.navigator.TableNavigator;

public abstract class ExpandableDataTable<T extends Serializable> extends Panel {

	private static final long serialVersionUID = 1L;

	private static final int DEFAULT_ITEMS_PER_PAGE = 20;

	private final List<DataTableColumn<T>> columns;
	private final SortableDataProvider<T, String> dataProvider;
	private final TransparentWebMarkupContainer tableContainer;

	public ExpandableDataTable(String id, List<DataTableColumn<T>> columns,
			SortableDataProvider<T, String> dataProvider) {
		super(id);
		this.columns = columns;
		this.dataProvider = dataProvider;
		this.tableContainer = new TransparentWebMarkupContainer(
				"tableContainer");
	}

	@Override
	protected void onInitialize() {
		super.onInitialize();

		this.tableContainer.setOutputMarkupId(true);
		this.add(this.tableContainer);

		RefreshingView<?> tableHeader = buildTableHeader();
		ExpandableDataTable.this.tableContainer.add(tableHeader);

		DataView<?> tableBody = buildTableBody();
		ExpandableDataTable.this.tableContainer.add(tableBody);

		PagingNavigator tableNavigator = buildTableNavigator(tableBody);
		this.add(tableNavigator);

	}

	private RefreshingView<?> buildTableHeader() {
		RefreshingView<DataTableColumn<T>> tableColumnHeaderRepeater = new RefreshingView<DataTableColumn<T>>(
				"tableColumnHeaderRepeater") {

			private static final long serialVersionUID = 1L;

			@Override
			protected void populateItem(Item<DataTableColumn<T>> item) {
				DataTableColumn<T> modelObject = item.getModelObject();
				Component tableColumnHeaderContent = getTableColumnHeaderContent(modelObject);
				item.add(tableColumnHeaderContent);
			}

			@Override
			protected Iterator<IModel<DataTableColumn<T>>> getItemModels() {
				Iterator<DataTableColumn<T>> iterator = columns.iterator();
				return new ModelIteratorAdapter<DataTableColumn<T>>(iterator) {

					@Override
					protected IModel<DataTableColumn<T>> model(
							DataTableColumn<T> object) {
						return Model.of(object);
					}

				};
			}

		};
		return tableColumnHeaderRepeater;
	}

	private Component getTableColumnHeaderContent(DataTableColumn<T> column) {
		String propertyNameForSort = column.getPropertyExpression();
		IModel<?> textModel = column.getDisplayModel();
		if (propertyNameForSort == null) {
			return new LabelHeader("tableColumnHeaderContent", textModel);
		}
		return new SortingHeader<String>("tableColumnHeaderContent",
				propertyNameForSort, dataProvider, textModel);
	}

	private DataView<?> buildTableBody() {
		DataView<T> tableBody = new DataView<T>("tableBody", dataProvider,
				ExpandableDataTable.DEFAULT_ITEMS_PER_PAGE) {

			private static final long serialVersionUID = 1L;

			@Override
			protected void populateItem(Item<T> item) {
				final IModel<T> componentModel = item.getModel();
				final T modelObject = item.getModelObject();

				buildTableBodyCell(componentModel, item);

				buildExpandableContent(modelObject, item);

			}

		};
		return tableBody;
	}

	private void buildTableBodyCell(final IModel<T> componentModel, Item<T> item) {
		RefreshingView<? extends DataTableColumn<T>> tableBodyCell = new RefreshingView<DataTableColumn<T>>(
				"tableBodyCell") {

			private static final long serialVersionUID = 1L;

			@Override
			protected Iterator<IModel<DataTableColumn<T>>> getItemModels() {
				Iterator<DataTableColumn<T>> iterator = columns.iterator();
				return new ModelIteratorAdapter<DataTableColumn<T>>(iterator) {

					@Override
					protected IModel<DataTableColumn<T>> model(
							DataTableColumn<T> object) {
						return Model.of(object);
					}

				};
			}

			@Override
			protected void populateItem(Item<DataTableColumn<T>> item) {
				DataTableColumn<T> modelObject = item.getModelObject();
				item.add(modelObject.getComponent("tableBodyCellContent",
						componentModel));
			}

		};
		item.add(tableBodyCell);
	}

	private PagingNavigator buildTableNavigator(final DataView<?> view) {
		IModel<String> totalItemsModel = new Model<String>() {

			private static final long serialVersionUID = 1L;

			@Override
			public String getObject() {
				return String.format("%s %s", view.getItemCount(), "Entries");
			}
		};
		List<DropDownEntry> entries = Arrays.asList(new DropDownEntry(5,
				"entries per page"), new DropDownEntry(10, "entries per page"),
				new DropDownEntry(20, "entries per page"), new DropDownEntry(
						40, "entries per page"));
		return new TableNavigator("navigator", view, totalItemsModel, entries,
				entries.get(2)) {

			private static final long serialVersionUID = 1L;

			@Override
			public void onDropDownValueChanged(AjaxRequestTarget target, DropDownEntry entry) {
				view.setItemsPerPage(entry.getEntriesPerPageNumber());
				target.add(ExpandableDataTable.this.tableContainer);
			}

		};
	}

	private void buildExpandableContent(final T modelObject, final Item<T> item) {
		TransparentWebMarkupContainer tableRowExpandableContentRow = new TransparentWebMarkupContainer(
				"tableRowExpandableContentRow");
		int numberOfColumns = this.columns.size();
		tableRowExpandableContentRow.add(new AttributeAppender("colspan",
				numberOfColumns));

		Component tableRowExpandableContent = ExpandableDataTable.this
				.getExpandableContent("tableRowExpandableContent", modelObject);
		tableRowExpandableContent.setOutputMarkupId(true);
		tableRowExpandableContent.setOutputMarkupPlaceholderTag(true);
		tableRowExpandableContent.setVisibilityAllowed(true);
		tableRowExpandableContent.setVisible(false);

		TransparentWebMarkupContainer tableRowExpandableContentWrapper = new TransparentWebMarkupContainer(
				"tableRowExpandableContentWrapper");
		tableRowExpandableContentWrapper.setOutputMarkupId(true);
		tableRowExpandableContentWrapper.add(tableRowExpandableContent);

		tableRowExpandableContentRow.add(tableRowExpandableContentWrapper);
		tableRowExpandableContentRow.setOutputMarkupId(true);

		String tableRowExpandableContentWrapperID = tableRowExpandableContentWrapper
				.getMarkupId();
		buildExpandableContentButton(tableRowExpandableContent,
				tableRowExpandableContentWrapperID, item);

		item.add(tableRowExpandableContentRow);
	}

	private void buildExpandableContentButton(
			final Component expandableContent,
			final String expandableContentWrapperID, final Item<T> item) {
		AjaxLink<Void> expandableContentButton = new AjaxLink<Void>(
				"expandableContentButton") {

			private static final long serialVersionUID = 1L;

			@Override
			public void onClick(AjaxRequestTarget target) {
				boolean visible = expandableContent.isVisible();
				expandableContent.setVisible(!visible);
				target.add(expandableContent);

				String JS = String.format("ExpandableDataTable.animate(%s)",
						expandableContentWrapperID);
				target.appendJavaScript(JS);
			}

		};
		item.add(expandableContentButton);
	}

	@Override
	public void renderHead(IHeaderResponse response) {
		super.renderHead(response);
		response.render(JavaScriptHeaderItem
				.forReference(new PackageResourceReference(
						ExpandableDataTable.class,
						"../resources/js/ExpandableDataTable.js")));
		response.render(CssHeaderItem.forReference(new CssResourceReference(
				ExpandableDataTable.class,
				"../resources/css/expandableDataTableStyle.css")));
	}

	public abstract Component getExpandableContent(String componentID,
			T modelObject);

}
